#include "config.h"

#define DBG_SUBSYS S_LIBSTORAGE

#include "sysy_lib.h"
#include "lsv.h"
#include "coroutine.h"

#include "lsv_wbuffer.h"
#include "lsv_wbuffer_internal.h"

// extern int _align_page_write(void *arg);

typedef struct {
        hashtable_t ht;
        uint64_t lba;
        co_mq_t mq;
} lba_fifo_t;

static uint32_t __key_func(const void *k) {
        const uint64_t *lba = k;
        return (*lba) % 1048576;
}

static int __cmp_func(const void *s1, const void *s2) {
        const lba_fifo_t *e1 = s1;
        const uint64_t *lba = s2;
        if( e1->lba < *lba)
                return -1;
        else if (e1->lba > *lba)
                return 1;
        return 0;
}

int lba_kv_init(hashtable_t *_ht) {
        hashtable_t ht;

        *_ht = NULL;

        ht = hash_create_table(__cmp_func, __key_func, "LBA");

        *_ht = ht;
        return 0;
}

int lba_kv_destroy(hashtable_t ht) {
        // TODO
        (void) ht;
        return 0;
}

static void __free_func(void *arg) {
        lba_fifo_t *e = arg;
        static uint64_t free_count = 0;

        free_count++;
        DINFO("lba %llu %p free %llu\n", (LLU)e->lba, e, (LLU)free_count);

        YASSERT(e == e->mq.swf_arg);
        YASSERT(e->mq.queue.count == 0);
        // co_mq_destroy(&e1->mq);

        yfree((void **)&e);
}

static int _mq_swf(void *arg) {
        // TODO if last one, delete from tree
        int ret;
        lba_fifo_t *fifo = arg;
        lba_fifo_t *e;

        YASSERT(fifo->mq.queue.count == 0);

        ret = hash_table_remove(fifo->ht, &fifo->lba, (void **)&e);
        YASSERT(ret == 0);
        YASSERT(fifo == e);
        __free_func(e);

        return 0;
}

int lba_kv_insert(hashtable_t ht, co_func_t func, wbuf_io_frag_t *io_frag, int need_free) {
        int ret;
        const char *name = "LBA";
        lba_fifo_t *e;

        YASSERT(io_frag->lba % LSV_PAGE_SIZE == 0);

        e = hash_table_find(ht, (void *)&io_frag->lba);
        if (e) {
                co_mq_offer(&e->mq, name, func, io_frag, need_free);
        } else {
                //
                ret = ymalloc((void **)&e, sizeof(lba_fifo_t));
                if (unlikely(ret))
                        YASSERT(0);

                e->ht = ht;
                e->lba = io_frag->lba;

                co_mq_init(&e->mq, name);
                e->mq.swf = _mq_swf;
                e->mq.swf_arg = e;

                ret = hash_table_insert(ht, (void *)e, &e->lba, 0);

                co_mq_offer(&e->mq, name, func, io_frag, need_free);
        }

        // TODO
        static uint64_t pages = 0;
        pages++;
        DINFO("lba %llu %p page %llu size %u depth %u\n", (LLU)io_frag->lba, e, (LLU)pages,
               ht->num_of_entries, e->mq.queue.count);

        return 0;
}
